package xml;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.io.File;
import java.io.IOException;

import models.HibernateDBGestion;
import models.HibernateDocument;
import models.HibernateOccurence;
import models.HibernatePosition;
import models.HibernateTags;
import models.HibernateWord;

import org.hibernate.HibernateException;
import org.hibernate.NonUniqueResultException;
import org.hibernate.exception.DataException;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

public class XMLParser {
	private HibernateDocument currentDoc;
    private Element root;
    private long tailleDoc;
    private ArrayList<HibernateWord> wordList;
    private ArrayList<HibernateTags> tagList;
	private ArrayList<HibernateOccurence> occList;
	private ArrayList<HibernatePosition> positionList;
	private HashMap<Integer, String> stopliste;
	private HashMap<String, Integer> indexTag;
	private HibernateDBGestion manager;
    
	/**
	 * The main constructor of document parser
	 * @param hdoc - the document to parse
	 * @param stopliste - the word that we don't want to put in database
	 */
	public XMLParser(HibernateDocument hdoc, HashMap<Integer, String> stopliste) {
		this.currentDoc = hdoc;
		SAXBuilder builder = new SAXBuilder();
        try {
        	Document doc = builder.build(new File(hdoc.getNom()));
        	this.root = doc.getRootElement();
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}        
        this.tailleDoc = 0;
        this.stopliste = stopliste;
        this.indexTag = new HashMap<String,Integer>();
        this.wordList = new ArrayList<HibernateWord>();
        this.tagList = new ArrayList<HibernateTags>();
        this.occList = new ArrayList<HibernateOccurence>();
        this.positionList = new ArrayList<HibernatePosition>();
        this.manager =  new HibernateDBGestion();
	}
	
	/**
	 * @return the size of the document
	 */
	public long getTailleDoc() {
		return tailleDoc;
	}
	
	/**
	 * @param paragraph - a targeted text
	 * @return list of the word of that paragraph
	 */
	private StringTokenizer wordExtract(String paragraph){
		 return new StringTokenizer(paragraph,"?!,. \n'():;[]-\\");
	}
	
	/**
	 * @param word - the targeted word
	 * @return the object of this word
	 */
	private HibernateWord wordExist(String word){
		for (HibernateWord w : wordList){
			if(word.equals(w.getMot())){
				return w;
			}
		}
		return null;
		
	}
	
	/**
	 * @param e - an element of the XML document
	 * @param xPathFather - the xPath of the father tag
	 */
	private void parser(Element e, String xPathFather, HashMap<String, Integer> index, HibernateTags father){
		Element current;
		HashMap <String, Integer> indexLocal = new HashMap<String,Integer>();
		List<Element> children= e.getChildren();
		StringTokenizer st;
		String tagName = e.getName();
		HibernateTags tag = new HibernateTags();
		tag.setDocID(this.currentDoc);
		tag.setTypeB(tagName);
		int taille=0;
		int ind;
		if(!index.containsKey(tagName)){
			index.put(tagName, 1);
			tagName = tagName + "[1]";
		} else {
			ind = index.remove(tagName);
			ind++;
			index.put(tagName, ind);
			tagName = tagName + "["+ind+"]";
		}
		tag.setxPath(xPathFather+"/"+tagName);
		tag.setTagFather(father);
		tagList.add(tag);
		
		if (!children.isEmpty()){
			for(int i=0;i<children.size();i++){
				current = (Element)children.get(i);
				this.parser(current, xPathFather+"/"+tagName,indexLocal,tag);
			}
		} else {
			st = wordExtract(e.getText());
			HashMap<HibernateWord, Integer> freq =  new HashMap<HibernateWord, Integer>();
			int pos = 1; //Start with first position
			while(st.hasMoreTokens()){
				String w = st.nextToken().toLowerCase();
				taille++;
				HibernateWord word = null;
				w = WordLemmatiser.lemmatize(w);
				if(!stopliste.containsValue(w)){ //Ignore the word in the stop list
					word = wordExist(w);
					if(word==null){
						try{
							word = manager.getWordByString(w);
						}catch(NonUniqueResultException ex){
							List l = manager.getListByString(w);
							Iterator i = l.iterator();
							boolean found = false;
							while(i.hasNext()&&(!found)){
								word = (HibernateWord)i.next();
								if (w.equals(word.getMot())){
									found = true;
								}
							}
						}catch(HibernateException ex){
							System.out.println(w);
							System.out.println(ex.toString());
						}
						if(word == null){
							word = new HibernateWord();
							word.setMot(w);
							wordList.add(word);
						}
					}
					if(!freq.containsKey(word)){												
						freq.put(word, 1);
					}else{
						int f = freq.remove(word);
						f++;
						freq.put(word, f);
					}
					HibernatePosition hPos = new HibernatePosition();
					hPos.setMotId(word);
					hPos.setParaId(tag);
					hPos.setPosition(pos);
					positionList.add(hPos);
				}
				pos++;
			}
			tag.setTaille(taille);
			this.tailleDoc += taille;
			for(HibernateWord w : freq.keySet()){
				HibernateOccurence hOcc = new HibernateOccurence();
				hOcc.setParaId(tag);
				hOcc.setMotId(w);
				hOcc.setFreq(freq.get(w));
				occList.add(hOcc);
			}
		}
	}
	
	public void parserDoc(){		
		manager.startConnection();
		manager.saveOrUpdateDoc(currentDoc);
		this.parser(root,"",indexTag,null);
		manager.saveOrUpdateTagList(tagList);
		manager.saveOrUpdateWordList(wordList);
		manager.saveOrUpdatePosList(positionList);
		manager.saveOrUpdateOccList(occList);
		manager.endConnection();
	}
}
